package cn.ylz.ylmall.account.service.impl;

import cn.ylz.ylmall.account.dao.cache.IUserCacheRepository;
import cn.ylz.ylmall.account.dao.persist.repository.IUserRepository;
import cn.ylz.ylmall.account.pojo.entity.User;
import cn.ylz.ylmall.account.pojo.param.UserRegParam;
import cn.ylz.ylmall.account.pojo.param.UserUpdateParam;
import cn.ylz.ylmall.account.pojo.po.UserLoginInfoPO;
import cn.ylz.ylmall.account.pojo.vo.*;
import cn.ylz.ylmall.account.security.CurrentPrincipal;
import cn.ylz.ylmall.common.consts.HttpConsts;
import cn.ylz.ylmall.common.util.MailUtils;
import com.alibaba.fastjson.JSON;
import cn.ylz.ylmall.common.ex.ServiceException;
import cn.ylz.ylmall.account.security.CustomUserDetails;
import cn.ylz.ylmall.common.web.ServiceCode;
import cn.ylz.ylmall.account.dao.persist.repository.IUserRoleRepository;
import cn.ylz.ylmall.account.pojo.entity.UserRole;
import cn.ylz.ylmall.account.pojo.param.UserLoginParam;
import cn.ylz.ylmall.account.pojo.param.UserRegParamAdmin;
import cn.ylz.ylmall.account.service.IUserService;
import com.google.code.kaptcha.Producer;
import io.jsonwebtoken.Jwts;
import io.jsonwebtoken.SignatureAlgorithm;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.core.ValueOperations;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.context.SecurityContext;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.stereotype.Service;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;
import java.io.Serializable;
import java.util.*;

@Slf4j
@Service
public class UserServiceImpl implements IUserService, HttpConsts {

    @Value("${ylmall.dao.default-query-page-size}")
    private Integer defaultQueryPageSize;
    @Value("${ylmall.jwt.secret-key}")
    private String secretKey;

    @Value("${ylmall.jwt.duration-in-minute}")
    private long durationInMinute;
    @Autowired
    private PasswordEncoder passwordEncoder;
    @Autowired
    private AuthenticationManager authenticationManager;
    @Autowired
    private IUserRepository userRepository;
    @Autowired
    private IUserRoleRepository userRoleRepository;
    @Autowired
    private IUserCacheRepository cacheRepository;
    @Autowired
    private IUserCacheRepository userCacheRepository;

    @Override
    public UserLoginResultVO login(UserLoginParam userLoginParam, HttpServletRequest req, HttpServletResponse resp) {
        /*log.debug("用户登录信息, {}", userLoginParam);
        String code = userLoginParam.getCode();
        String verify_code = (String) req.getSession().getAttribute("verifyCode");
        log.debug("code = {},verify_code = {}", code,verify_code);
        if (verify_code == null) {
            String message = "验证码已过期，请刷新验证码";
            log.warn(message);
            throw new ServiceException(ServiceCode.ERROR_VERIFY_LONG, message);
        }
        if ("".equals(code) || !verify_code.equalsIgnoreCase(code)) {
            String message = "验证码填写错误";
            log.warn(message);
            throw new ServiceException(ServiceCode.ERROR_VERIFY, message);
        } else {*/
            Authentication authentication =
                    new UsernamePasswordAuthenticationToken(userLoginParam.getUsername(), userLoginParam.getPassword());
            Authentication authenticateResult = authenticationManager.authenticate(authentication);
            log.debug("验证用户登录成功，返回的认证结果：{}", authenticateResult);
            Object principal = authenticateResult.getPrincipal();
            log.debug("从认证结果中获取当事人：{}", principal);
            CustomUserDetails userDetails = (CustomUserDetails) principal;
            log.debug("准备将认证信息结果存入到SecurityContext中……");
            SecurityContext securityContext = SecurityContextHolder.getContext();
            securityContext.setAuthentication(authenticateResult);
            log.debug("已经将认证信息存入到SecurityContext中，登录业务处理完成！");
            Collection<GrantedAuthority> authorities = userDetails.getAuthorities();
            String authoritiesJsonString = JSON.toJSONString(authorities);
            log.debug("当前登录用户的权限列表,{}", authoritiesJsonString);
            Long id = userDetails.getId();
            String username = userDetails.getUsername();
            String avatar = userDetails.getAvatar();
            String email = userDetails.getEmail();
            String phone = userDetails.getPhone();
            Map<String, Object> claims = new HashMap<>();
            claims.put("id", id);
            claims.put("username", username);
            // claims.put("authoritiesJsonString", authoritiesJsonString);
            Date date = new Date(System.currentTimeMillis() + 1L * durationInMinute * 60 * 1000);
            String jwt = Jwts.builder()
                    // Header
                    .setHeaderParam("alg", "HS256")
                    .setHeaderParam("typ", "JWT")
                    // Payload
                    .setClaims(claims)
                    .setExpiration(date)
                    // Verify Signature
                    .signWith(SignatureAlgorithm.HS256, secretKey)
                    // Done
                    .compact();
            // 将权限列表存入Redis
            UserLoginInfoPO userLoginInfoPO = new UserLoginInfoPO();
            userLoginInfoPO.setIp(req.getRemoteAddr());
            userLoginInfoPO.setUserAgent(req.getHeader(HEADER_USER_AGENT));
            userLoginInfoPO.setAuthoritiesJsonString(authoritiesJsonString);
            cacheRepository.saveLoginInfo(jwt, userLoginInfoPO);
            // 将用户状态存入Redis
            cacheRepository.saveEnableByUserId(id, 1);
            UserLoginResultVO userLoginResultVO = new UserLoginResultVO().setId(id).setUsername(username).setAvatar(avatar).setPhone(phone).setEmail(email).setToken(jwt);
            return userLoginResultVO;
        }
//    }


        @Override
        public void reg (UserRegParamAdmin userRegParam){
            // 检查用户名是否被占用
            {
                String username = userRegParam.getUsername();
                int count = userRepository.countByUsername(username);
                if (count > 0) {
                    String message = "添加用户失败，用户名已经被占用！";
                    log.warn(message);
                    throw new ServiceException(ServiceCode.ERROR_CONFLICT, message);
                }
            }

            // 检查手机号码是否被占用
            {
                String phone = userRegParam.getPhone();
                int count = userRepository.countByPhone(phone);
                if (count > 0) {
                    String message = "添加用户失败，手机号码已经被占用！";
                    log.warn(message);
                    throw new ServiceException(ServiceCode.ERROR_CONFLICT, message);
                }
            }

            // 检查电子邮箱是否被占用
            {
                String email = userRegParam.getEmail();
                int count = userRepository.countByEmail(email);
                if (count > 0) {
                    String message = "添加用户失败，电子邮箱已经被占用！";
                    log.warn(message);
                    throw new ServiceException(ServiceCode.ERROR_CONFLICT, message);
                }
            }

            User user = new User();
            BeanUtils.copyProperties(userRegParam, user);
            String rawPassword = user.getPassword();
            String encodedPassword = passwordEncoder.encode(rawPassword);
            user.setPassword(encodedPassword);
            int rows = userRepository.insert(user);
            if (rows != 1) {
                String message = "添加用户失败，服务器忙，请稍后再尝试！";
                log.warn(message);
                throw new ServiceException(ServiceCode.ERROR_INSERT, message);
            }

            Long[] roleIds = userRegParam.getRoleIds();
            List<UserRole> userRoleList = new ArrayList<>();
            for (Long roleId : roleIds) {
                UserRole userRole = new UserRole();
                userRole.setUserId(user.getId());
                userRole.setRoleId(roleId);
                userRoleList.add(userRole);
            }
            rows = userRoleRepository.insertBatch(userRoleList);
            if (rows < 1) {
                String message = "添加用户失败，服务器忙，请稍后再尝试！";
                log.warn(message);
                throw new ServiceException(ServiceCode.ERROR_INSERT, message);
            }
        }

        @Override
        public PageData<UserListItemVO> list (Integer pageNum){
            PageData<UserListItemVO> pageData = userRepository.list(pageNum, defaultQueryPageSize);
            return pageData;
        }

        @Override
        public PageData<UserListItemVO> list (Integer pageNum, Integer pageSize){
            PageData<UserListItemVO> pageData = userRepository.list(pageNum, pageSize);
            return pageData;
        }

        @Override
        public void delete (Long id){
            UserStandardVO queryResult = userRepository.getStandardById(id);
            if (queryResult == null) {
                String message = "删除用户失败，尝试访问的数据不存在！";
                log.warn(message);
                throw new ServiceException(ServiceCode.ERROR_NOT_FOUND, message);
            }
            int rows = userRepository.delete(id);
            if (rows != 1) {
                String message = "删除用户失败，服务器忙，请稍后再尝试！";
                log.warn(message);
                throw new ServiceException(ServiceCode.ERROR_DELETE, message);
            }

            log.debug("即将执行删除关联数据，参数：{}", id);
            rows = userRoleRepository.deleteByUserId(id);
            if (rows < 1) {
                String message = "删除用户失败，服务器忙，请稍后再尝试！";
                log.warn(message);
                throw new ServiceException(ServiceCode.ERROR_DELETE, message);
            }
        }

        @Override
        public UserStandardVO getStandardById (Long id){
            UserStandardVO userStandardVO = userRepository.getStandardById(id);
            if (userStandardVO == null) {
                throw new ServiceException(ServiceCode.ERROR_NOT_FOUND, "此用户不存在");
            }
            return userStandardVO;
        }

        @Override
        public void update (Long id, UserUpdateParam userUpdateParam, CurrentPrincipal currentPrincipal){
            Long userId = currentPrincipal.getId();
            UserStandardVO queryResult = userRepository.getStandardById(id);

            if (queryResult == null) {
                throw new ServiceException(ServiceCode.ERROR_NOT_FOUND, "修改信息失败，该用户不存在");
            }

            User user = new User();
            BeanUtils.copyProperties(userUpdateParam, user);
            user.setId(id);
            boolean isAdmin = isAdmin(userId);
            if (!userId.equals(queryResult.getId()) && !isAdmin) {
                throw new ServiceException(ServiceCode.ERROR_FORBIDDEN, "修改用户信息失败，无操作权限");
            }

            int rows = userRepository.update(user);
            if (rows != 1) {
                throw new ServiceException(ServiceCode.ERROR_UPDATE, "修改用户信息失败，服务器忙，请稍后再尝试！");
            }
        }

        private boolean isAdmin (Long userId){
            List<UserRoleVO> userRoles = userRepository.roleById(userId);
            return userRoles.stream().anyMatch(userRoleVO -> userRoleVO.getId() == 1L);
        }

        @Override
        public List<UserPermissionVO> PermissionsById (Long id){
            return userRepository.PermissionsById(id);
        }

        @Override
        public void regUser (UserRegParam userRegParam){
            // 检查用户名是否被占用
            {
                String username = userRegParam.getUsername();
                int count = userRepository.countByUsername(username);
                if (count > 0) {
                    String message = "添加用户失败，用户名已经被占用！";
                    log.warn(message);
                    throw new ServiceException(ServiceCode.ERROR_CONFLICT, message);
                }
            }

            // 检查手机号码是否被占用
            {
                String phone = userRegParam.getPhone();
                int count = userRepository.countByPhone(phone);
                if (count > 0) {
                    String message = "添加用户失败，手机号码已经被占用！";
                    log.warn(message);
                    throw new ServiceException(ServiceCode.ERROR_CONFLICT, message);
                }
            }

            // 检查电子邮箱是否被占用
            {
                String email = userRegParam.getEmail();
                int count = userRepository.countByEmail(email);
                if (count > 0) {
                    String message = "添加用户失败，电子邮箱已经被占用！";
                    log.warn(message);
                    throw new ServiceException(ServiceCode.ERROR_CONFLICT, message);
                }
            }

            // 验证邮箱验证码
            {
                String emailVerifyCode = userCacheRepository.getEmailVerifyCode(userRegParam.getEmail());
                String code = userRegParam.getCode();
                if ("".equals(code) || !emailVerifyCode.equalsIgnoreCase(code)) {
                    String message = "验证码填写错误";
                    log.warn(message);
                    throw new ServiceException(ServiceCode.ERROR_VERIFY, message);
                }
            }

            User user = new User();
            BeanUtils.copyProperties(userRegParam, user);
            String rawPassword = user.getPassword();
            String encodedPassword = passwordEncoder.encode(rawPassword);
            user.setPassword(encodedPassword);
            user.setEnable(1);
            int rows = userRepository.insert(user);
            if (rows != 1) {
                String message = "添加用户失败，服务器忙，请稍后再尝试！";
                log.warn(message);
                throw new ServiceException(ServiceCode.ERROR_INSERT, message);
            }
            List<UserRole> userRoleList = new ArrayList<>();
            UserRole userRole = new UserRole();
            userRole.setUserId(user.getId());
            userRole.setRoleId(3L);
            userRoleList.add(userRole);
            rows = userRoleRepository.insertBatch(userRoleList);
            if (rows < 1) {
                String message = "添加用户失败，服务器忙，请稍后再尝试！";
                log.warn(message);
                throw new ServiceException(ServiceCode.ERROR_INSERT, message);
            }
        }

        @Override
        public void setEnable (Long id){
            updateEnableById(id, 1);
        }

        @Override
        public void setDisable (Long id){
            updateEnableById(id, 0);
        }

        @Override
        public List<UserRoleVO> rolesById (Long userId){
            return userRepository.roleById(userId);
        }

        @Override
        public PageData<UserListItemVO> listAdmin (Integer pageNum){
            PageData<UserListItemVO> pageData = userRepository.listAdmin(pageNum, defaultQueryPageSize);
            return pageData;
        }


    private void updateEnableById (Long id, Integer enable){
            UserStandardVO queryResult = userRepository.getStandardById(id);
            if (queryResult == null) {
                String message = ENABLE_TEXT[enable] + "用户失败,用户不存在";
                log.warn(message);
                throw new ServiceException(ServiceCode.ERROR_NOT_FOUND, message);
            }
            if (queryResult.getEnable().equals(enable)) {
                String message = ENABLE_TEXT[enable] + "用户失败,用户已经处于" + ENABLE_TEXT[enable] + "状态";
                log.warn(message);
                throw new ServiceException(ServiceCode.ERROR_CONFLICT, message);
            }
            User user = new User();
            user.setId(id);
            user.setEnable(enable);
            int rows = userRepository.update(user);
            if (rows != 1) {
                String message = ENABLE_TEXT[enable] + "用户失败，服务器忙，请稍后再次尝试！";
                log.warn(message);
                throw new ServiceException(ServiceCode.ERROR_UPDATE, message);
            }
            Integer enableByUserId = cacheRepository.getEnableByUserId(id);
            if (enableByUserId != null) {
                cacheRepository.saveEnableByUserId(id, enable);
            }


        }


    }
